#include "modeling.h"

Modeling::Modeling(Scene *_scene){
    scene = _scene;
}


Modeling::~Modeling(){

}


void Modeling::addFace(Face face, int objectId){
    objects[objectId].addFace(face);
}


void Modeling::addObject(Object obj){
    objects[ obj.getId() ] = obj;
}


map<int, Object>& Modeling::getObjects(){
    return this->objects;
}


Scene* Modeling::getScene(){
    return scene;
}


int Modeling::isSelectedVertex(int objId, int elemId){
    if ( objects[objId].hasVertex(elemId) ){
        for (int i = 0; i < selectedVertices.size(); i++){
            if (selectedVertices[i] == elemId){
                return i;
            }
        }
    }
    return -1;
}


void Modeling::selectVertex(int objId, int elemId){
    if ( objects[objId].hasVertex(elemId) ){
        Vertex &v = objects[objId].getVertex(elemId);

        int found = isSelectedVertex(objId, elemId);
        if (found < 0){
            v.setColor(0.0, 0.0, 1.0);
            selectedVertices.push_back(elemId);
        }
        else{
            v.setColor(0.0, 1.0, 0.0);
            selectedVertices.erase(selectedVertices.begin()+found);
        }
        updateScene();
    }
}


int Modeling::isSelectedEdge(int objId, int elemId){
    if ( objects[objId].hasEdge(elemId) ){
        for (int i = 0; i < selectedEdges.size(); i++){
            if (selectedEdges[i] == elemId){
                return i;
            }
        }
    }
    return -1;
}


void Modeling::selectEdge(int objId, int elemId){
    if ( objects[objId].hasEdge(elemId) ){
        Edge &e = objects[objId].getEdge(elemId);

        int found = isSelectedEdge(objId, elemId);
        if (found < 0){
            e.setColor(1.0, 1.0, 0.0);
            selectedEdges.push_back(elemId);
        }
        else{
            e.setColor(1.0, 0.0, 0.0);
            selectedEdges.erase(selectedEdges.begin()+found);
        }
        updateScene();
    }
}


int Modeling::isSelectedFace(int objId, int elemId){
    if ( objects[objId].hasFace(elemId) ){
        for (int i = 0; i < selectedFaces.size(); i++){
            if (selectedFaces[i] == elemId){
                return i;
            }
        }
    }
    return -1;
}


Face& Modeling::selectFace(int objId, int elemId){
    if ( objects[objId].hasFace(elemId) ){
        Face &f = objects[objId].getFace(elemId);

        int found = isSelectedFace(objId, elemId);
        if (found < 0){
            f.setColor(0.0, 1.0, 1.0);
            selectedFaces.push_back(elemId);
        }        
        else{
            f.setColor(0.0, 0.5, 0.5);
            selectedFaces.erase(selectedFaces.begin()+found);
        }
        updateScene();
        return f;
    }
}        


void Modeling::testPrisma(){
    Object prisma = factory.createObject();

    Vertex v1 = factory.createVertex(0.0, 0.0, 0.0, prisma.getId());
    Vertex v2 = factory.createVertex(3.0, 0.0, 0.0, prisma.getId());
    Vertex v3 = factory.createVertex(3.0, 0.0, 3.0, prisma.getId());
    
    Edge e1(v1, v2, prisma.getId());
    Edge e2(v2, v3, prisma.getId());
    Edge e3(v3, v1, prisma.getId());
    
    Face f = factory.createFace(prisma.getId());    
    f.addEdge(e1);
    f.addEdge(e2);
    f.addEdge(e3);
    prisma.addFace(f);
    addObject(prisma);
    
    createPrism(prisma.getId(), f.getId(), Point(0.0, 5.0, 0.0));
    
    updateScene();
}


void Modeling::createPrism(unsigned int objectId, unsigned int faceId, Point generatrix){
    Face &f = objects[objectId].getFace(faceId);
    Face topFace = factory.createFace(objectId);
    
    Edge top, up, down, firstDown;
    
    vector<Edge> edges = f.getEdges();
    for (int i = 0; i < edges.size(); i++){
        pair<Vertex, Vertex> &vertices = edges[i].getVertices();
    
        Vertex first = factory.createVertex(vertices.first + generatrix, objectId);
        Vertex second = factory.createVertex(vertices.second + generatrix, objectId);
        
        Edge top = factory.createEdge(first, second, objectId);
        topFace.addEdge(top);
        
        Face newFace = factory.createFace(objectId);
        
        if (i == 0){
            down = factory.createEdge(first, vertices.first, objectId);
            firstDown = down;
        }
        else{
            down = up;
        }
        
        if (i == edges.size() - 1){
            up = firstDown;
        }
        else{
            up   = factory.createEdge(vertices.second, second, objectId);
        }
        newFace.addEdge(edges[i]);
        newFace.addEdge(up);
        newFace.addEdge(top);
        newFace.addEdge(down);
        
        objects[objectId].addFace(newFace);

    }
    objects[objectId].addFace(topFace);
}  

void Modeling::createPyramid(unsigned int objectId, unsigned int faceId, Point generatrix){
    Face &f = objects[objectId].getFace(faceId);

    Vertex centroid;
    vector<Edge> edges = f.getEdges();
    for (int i = 0; i < edges.size(); i++){
        centroid += edges[i].getVertices().first;
    }
    centroid /= edges.size();
    Vertex top = factory.createVertex(centroid + generatrix, objectId);
        
    Edge up, down, firstDown;
    for (int i = 0; i < edges.size(); i++){
        pair<Vertex, Vertex> &vertices = edges[i].getVertices();
    
        Face newFace = factory.createFace(objectId);
        
        if (i == 0){
            down = factory.createEdge(top, vertices.first, objectId);
            firstDown = down;
        }
        else{
            down = up;
        }
        
        if (i == edges.size() - 1){
            up = firstDown;
        }
        else{
            up   = factory.createEdge(vertices.second, top, objectId);
        }
        newFace.addEdge(edges[i]);
        newFace.addEdge(up);
        newFace.addEdge(down);
        
        objects[objectId].addFace(newFace);

    }
}  


void Modeling::show(){
    printf("\n\nMODELING:\nObjects: %i",objects.size());
    for (map<int, Object>::iterator it = objects.begin(); it != objects.end(); it++){
        it->second.show();
    }
    
    printf("\n\nSKETCH: %i\n",scene->getSegments().size());

}


void Modeling::updateScene(){
    scene->setObjects(objects);
    scene->setUp();
    scene->updateEnviroment();
    scene->drawEnviroment();
    scene->drawObjects();
    scene->drawSpheres();
    scene->drawSegments();
}
